using NSQLObjects;
using NSQLObjects.Generator.AST;



using System;


namespace NSQLObjects.Generator.Syntax {
public partial class Parser {
	public const int _EOF = 0;
	public const int _ident = 1;
	public const int _number = 2;
	public const int _block = 3;
	public const int _dblock = 4;
	public const int _string = 5;
	public const int maxT = 39;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void TableDef() {
		while (StartOf(1)) {
			Define();
		}
		while (la.kind == 6 || la.kind == 11 || la.kind == 19) {
			Object();
		}
	}

	void Define() {
		if (la.kind == 22) {
			Namespace();
		} else if (la.kind == 23) {
			Connection();
		} else if (la.kind == 24) {
			Import();
		} else if (la.kind == 25) {
			CodeBlock();
		} else if (la.kind == 26) {
			Default();
		} else SynErr(40);
	}

	void Object() {
		if (la.kind == 6) {
			Entity();
		} else if (la.kind == 11) {
			Query();
		} else if (la.kind == 19) {
			Fetch();
		} else SynErr(41);
	}

	void Entity() {
		Expect(6);
		Expect(1);
		Entity eb = new Entity(t.val, G);
		AddEntity(eb);
		eb.CodeLine = t.line;
		
									
		if (la.kind == 7) {
			Get();
			string tn = null; 
			TableName(out tn);
			eb.TableName = tn; 
		} else if (la.kind == 8 || la.kind == 9) {
			eb.TableName = eb.EntityName; 
		} else SynErr(42);
		if (la.kind == 8) {
			Get();
			Expect(1);
			if (!eb.Inherits(t.val))
			SemErr("Undefined base class {0}".F(t.val));	
			          
		} else if (la.kind == 9) {
		} else SynErr(43);
		Expect(9);
		DBProperty newField = null; 
		while (StartOf(2)) {
			FieldDef(out newField);
			eb.Properties.Add(newField); 
		}
		Expect(10);
	}

	void Query() {
		Expect(11);
		Expect(1);
		Query qb = new Query(t.val,G); 
		string queryBody = null;
		string orderBy = null;
		qb.CodeLine = t.line;
		
		if (la.kind == 12) {
			Get();
			QueryParam(qb);
			while (la.kind == 13) {
				Get();
				QueryParam(qb);
			}
			Expect(14);
		}
		if (la.kind == 8) {
			Get();
			Expect(1);
			qb.QueryInherit = t.val; 
		}
		Block(out queryBody);
		qb.QuerySQL = queryBody; 
		if (la.kind == 15) {
			Get();
			if (la.kind == 16) {
				Get();
			}
			Block(out orderBy);
			qb.OrderBy = orderBy; 
		}
		if (la.kind == 17) {
			Get();
			Expect(1);
			qb.RowClass = t.val; 
		}
		if (la.kind == 8) {
			Get();
			Expect(1);
			if (!qb.RowInherits(t.val))
			SemErr("Can't locate basetype {0}".F(t.val)); 
			
		}
		if (la.kind == 7) {
			QueryUpdate(qb);
		}
		if (la.kind == 9) {
			QueryProperties(qb);
		} else if (la.kind == 18) {
			Get();
		} else SynErr(44);
	}

	void Fetch() {
		Expect(19);
		Expect(1);
		Query qb = new Query(t.val,G); 
		qb.IsFetch = true;
		qb.RowClass = qb.QueryName;
		string queryBody = null;
		qb.CodeLine = t.line; 
		if (la.kind == 12) {
			Get();
			QueryParam(qb);
			while (la.kind == 13) {
				Get();
				QueryParam(qb);
			}
			Expect(14);
		}
		Block(out queryBody);
		qb.QuerySQL = queryBody; 
		if (la.kind == 8) {
			Get();
			Expect(1);
			if (!qb.RowInherits(t.val))
			SemErr("Can't locate basetype {0}".F(t.val)); 
			
		}
		if (la.kind == 7) {
			QueryUpdate(qb);
		}
		if (la.kind == 9) {
			QueryProperties(qb);
		} else if (la.kind == 18) {
			Get();
		} else SynErr(45);
	}

	void TableName(out string tn) {
		tn = null; 
		if (la.kind == 1) {
			Get();
			tn = t.val; 
		} else if (la.kind == 5) {
			Get();
			tn = G.TrimString(t.val); 
		} else SynErr(46);
	}

	void FieldDef(out DBProperty pr) {
		FieldRoles roles = FieldRoles.None;
		DBPropertyAttributes attr = DBPropertyAttributes.Public;
		bool fModifiers = false; 
		while (StartOf(3)) {
			if (la.kind == 28) {
				Get();
				roles |= FieldRoles.Readonly; fModifiers = true; 
			} else if (la.kind == 29) {
				Get();
				roles |= FieldRoles.Immutable; fModifiers = true; 
			} else if (la.kind == 30) {
				Get();
				roles |= FieldRoles.Compare; 
			} else if (la.kind == 31) {
				Get();
				attr = DBPropertyAttributes.Family; fModifiers = true; 
			} else {
				Get();
				attr = DBPropertyAttributes.Private; fModifiers = true; 
			}
		}
		Expect(1);
		string name = t.val; pr = null; 
		if (la.kind == 33) {
			Get();
			Expect(1);
			pr = GetDefault(t.val, true); 
			pr = pr.Clone(name);                     
			if (fModifiers)
				SemErr("Can't apply modifier(s) to like definition");
			              
		} else if (la.kind == 34) {
			Get();
			pr = new DBProperty(name); 
			pr.Roles = roles;
			pr.Attributes = attr;
			string customAttrs = null;
			string typeName = null;
			
			NsIdent(out typeName);
			pr.TypeName = typeName; 
			if (la.kind == 35) {
				Get();
				pr.TypeName += "?"; 
			} else if (la.kind == 36) {
				Get();
				pr.TypeName += "[]"; 
			} else if (StartOf(4)) {
			} else SynErr(47);
			if (la.kind == 37) {
				Get();
				Expect(5);
				pr.ComputeExpr = G.TrimString(t.val); 
			}
			if (la.kind == 3 || la.kind == 4) {
				Block(out customAttrs);
				pr.CustomAttributes = customAttrs; 
			}
		} else if (la.kind == 18) {
			pr = GetDefault(name, true); pr = pr.Clone(name); 
		} else SynErr(48);
		Expect(18);
	}

	void QueryParam(Query qb) {
		DBParameter paramDef = null; 
		ParamDef(out paramDef);
		qb.Parameters.Add(paramDef); 
	}

	void Block(out string blockCode) {
		blockCode = null; 
		if (la.kind == 3) {
			Get();
			blockCode = G.TrimBlock(t.val); 
		} else if (la.kind == 4) {
			Get();
			blockCode = G.TrimBlock(t.val); 
		} else SynErr(49);
	}

	void QueryUpdate(Query qb) {
		string tn = null; 
		Expect(7);
		TableName(out tn);
		qb.TableName = tn; 
		if (la.kind == 20) {
			Get();
			QueryUpdateRange(qb);
			while (la.kind == 13) {
				Get();
				QueryUpdateRange(qb);
			}
		}
	}

	void QueryProperties(Query qb) {
		DBProperty fieldDef = null; 
		Expect(9);
		while (StartOf(2)) {
			FieldDef(out fieldDef);
			fieldDef.QueryOnly = true;
			qb.Properties.Add(fieldDef); 
			
		}
		Expect(10);
	}

	void ParamDef(out DBParameter pr) {
		Expect(1);
		string name = t.val; pr = null; 
		Expect(34);
		pr = new DBParameter(name); 
		string typeName = null;
		
		NsIdent(out typeName);
		pr.TypeName = typeName; 
		if (la.kind == 35) {
			Get();
			pr.TypeName += "?"; 
		}
	}

	void QueryUpdateRange(Query qb) {
		Expect(1);
		string n1 = t.val; 
		if (la.kind == 21) {
			Get();
			Expect(1);
			qb.UpdateFieldRanges.Add(new Tuple<string,string>(n1, t.val)); 
		} else if (la.kind == 9 || la.kind == 13 || la.kind == 18) {
			qb.UpdateFieldRanges.Add(new Tuple<string,string>(n1, null)); 
		} else SynErr(50);
	}

	void Namespace() {
		Expect(22);
		string name = ""; 
		NsIdent(out name);
		G.NameSpace = name; 
		Expect(18);
	}

	void Connection() {
		Expect(23);
		Expect(5);
		G.ConnectionString = G.TrimString(t.val); 
		Expect(18);
	}

	void Import() {
		Expect(24);
		Expect(5);
		G.Import(G.TrimString(t.val)); 
		Expect(18);
	}

	void CodeBlock() {
		Expect(25);
		string code = ""; 
		Block(out code);
		G.PassThroughCode.AddCodeLines(code); 
	}

	void Default() {
		Expect(26);
		Expect(9);
		DBProperty defField = null; 
		while (StartOf(5)) {
			if (la.kind == 27) {
				Get();
				FieldDef(out defField);
				AddDefaultType(defField); 
			} else {
				FieldDef(out defField);
				AddDefault(defField); 
			}
		}
		Expect(10);
	}

	void NsIdent(out string name) {
		Expect(1);
		name = t.val; 
		while (la.kind == 38) {
			Get();
			Expect(1);
			name += "." + t.val; 
		}
	}



	public void Parse() {
		la = new Token();
		la.val = "";		
		Get();
		TableDef();

    Expect(0);
	}
	
	static readonly bool[,] set = {
		{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x},
		{x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,x,x,x, x,x,x,x, x},
		{x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,x,x,x, x,x,x,x, x},
		{x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x},
		{x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x, x,x,x,x, x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	public NSQLObjects.Generator.Compile.IReport ReportErrors { get; set; }
	public string FileName { get; set; }

	public void SynErr (int line, int col, int n) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "ident expected"; break;
			case 2: s = "number expected"; break;
			case 3: s = "block expected"; break;
			case 4: s = "dblock expected"; break;
			case 5: s = "string expected"; break;
			case 6: s = "\"entity\" expected"; break;
			case 7: s = "\"update\" expected"; break;
			case 8: s = "\"inherits\" expected"; break;
			case 9: s = "\"{\" expected"; break;
			case 10: s = "\"}\" expected"; break;
			case 11: s = "\"query\" expected"; break;
			case 12: s = "\"(\" expected"; break;
			case 13: s = "\",\" expected"; break;
			case 14: s = "\")\" expected"; break;
			case 15: s = "\"order\" expected"; break;
			case 16: s = "\"by\" expected"; break;
			case 17: s = "\"returns\" expected"; break;
			case 18: s = "\";\" expected"; break;
			case 19: s = "\"fetch\" expected"; break;
			case 20: s = "\"fields\" expected"; break;
			case 21: s = "\"-\" expected"; break;
			case 22: s = "\"namespace\" expected"; break;
			case 23: s = "\"connection\" expected"; break;
			case 24: s = "\"import\" expected"; break;
			case 25: s = "\"code\" expected"; break;
			case 26: s = "\"default\" expected"; break;
			case 27: s = "\"type\" expected"; break;
			case 28: s = "\"readonly\" expected"; break;
			case 29: s = "\"immutable\" expected"; break;
			case 30: s = "\"compare\" expected"; break;
			case 31: s = "\"protected\" expected"; break;
			case 32: s = "\"private\" expected"; break;
			case 33: s = "\"like\" expected"; break;
			case 34: s = "\":\" expected"; break;
			case 35: s = "\"?\" expected"; break;
			case 36: s = "\"*\" expected"; break;
			case 37: s = "\"compute\" expected"; break;
			case 38: s = "\".\" expected"; break;
			case 39: s = "??? expected"; break;
			case 40: s = "invalid Define"; break;
			case 41: s = "invalid Object"; break;
			case 42: s = "invalid Entity"; break;
			case 43: s = "invalid Entity"; break;
			case 44: s = "invalid Query"; break;
			case 45: s = "invalid Fetch"; break;
			case 46: s = "invalid TableName"; break;
			case 47: s = "invalid FieldDef"; break;
			case 48: s = "invalid FieldDef"; break;
			case 49: s = "invalid Block"; break;
			case 50: s = "invalid QueryUpdateRange"; break;

			default: s = "error " + n; break;
		}
		
		ReportErrors.Report(true, FileName, line, col, s);
		count++;
	}

	public void SemErr (int line, int col, string s) {
		ReportErrors.Report(true, FileName, line, col, s);
		count++;
	}
	
	public void SemErr (string s) {
		ReportErrors.Report(true, FileName, 0, 0, s);
		count++;
	}
	
	public void Warning (int line, int col, string s) {
		ReportErrors.Report(false, FileName, line, col, s);
	}
	
	public void Warning(string s) {
		ReportErrors.Report(false, FileName, 0, 0, s);
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}
}
